package org.osomit.sacct.account.utils;

import org.apache.commons.codec.binary.Base64;
import org.osomit.sacct.security.utils.SymmetricCipher;
import org.osomit.sacct.security.utils.Serializer;
import org.osomit.sacct.entity.ApplicationAccountRecordSet;
import org.osomit.sacct.builder.ApplicationAccountRecordSetBuilder;
import org.stringtree.json.JSONReader;

import java.io.*;
import java.util.Arrays;

import com.sdicons.json.mapper.JSONMapper;
import com.sdicons.json.model.JSONValue;

/**
 *
 * @author Jian Fang (John.Jian.Fang@gmail.com)
 *
 * Date: Jul 1, 2009
 */
public class AccountUtil {
    static int TYPICAL_SIZE = 1024;

    public static void main(String[] args)
    {
        String cmd = null;
        
        String firstName = "accounts.reg";
        String secondName = "accounts.reg";

        if(args.length == 3){
            cmd = args[0];
            firstName = args[1];
            secondName = args[2];
        }else{
            System.out.println("USAGE:  encrypt unencrpted_file_name encrypted_file_name");
            System.out.println("OR");
            System.out.println("USAGE:  decrypt encrypted_file_name unencrpted_file_name");
            System.out.println("");
            System.out.println("EXAMPLE: encryp accounts.reg.txt accounts.reg");
            System.out.println();
            System.exit(1);
        }

        if("encrypt".equalsIgnoreCase(cmd)){
            Console con = System.console();
            char[] pwd = con.readPassword("Enter account management password: ");
            String password = new String(pwd);
            char[] pwd2 = con.readPassword("Re-enter account management password: ");
            String password2 = new String(pwd2);
            if(password.equals(password2)){
                Arrays.fill(pwd2, ' '); // delete password from memory
                password2 = null;
                encrypt(password, firstName, secondName);
                Arrays.fill(pwd, ' '); // delete password from memory
                password.replaceAll(".", " "); 
                password = null;
            }else{
                System.out.println("Passwords you typed are not the same!");
                System.out.println();
                System.exit(1);
            }
        }else if("decrypt".equalsIgnoreCase(cmd)){
            Console con = System.console();
            char[] pwd = con.readPassword("Enter account management password: ");
            String password = new String(pwd);
            decrypt(password, firstName, secondName);
            Arrays.fill(pwd, ' '); // delete password from memory
            password.replaceAll(".", " ");
            password = null;
        }else{
            System.out.println("Invalid command: " + cmd);
            System.out.println();
            System.exit(1);
        }
    }

    protected static void encrypt(String password, String orignialFile, String encryptedFile){
        if (orignialFile == null || encryptedFile == null){
            System.out.println("Orignial_File_Name and Encrypted_File_Name cannot be null");
            return;
        }

        try {
            FileInputStream fin = new FileInputStream(orignialFile);
            BufferedReader br = new BufferedReader(new InputStreamReader(fin));
            String line;
            StringBuffer sb = new StringBuffer(TYPICAL_SIZE);
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }

            fin.close();
            br.close();

            if (sb.length() > 0) {
                JSONReader reader = new JSONReader();
                Object obj = reader.read(sb.toString());
                ApplicationAccountRecordSetBuilder builder = new ApplicationAccountRecordSetBuilder();
                ApplicationAccountRecordSet set = builder.build(obj);

                byte[] serialized = Serializer.compress(set.toJSON().toString().getBytes());
//                byte[] encrypted = SymmetricCipher.desEncrypt(password, serialized);
                byte[] encrypted = SymmetricCipher.aesEncrypt(password, serialized);
                String result = new String(Base64.encodeBase64(encrypted), "UTF8");

                FileOutputStream fout = new FileOutputStream(encryptedFile, false);
                fout.write(result.getBytes());
                fout.close();
            }
        }catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected static void decrypt(String password, String encryptedFile, String decryptedFile){
         if (encryptedFile == null || decryptedFile == null){
            System.out.println("Encrypted_File_Name and Decrypted_File_Name cannot be null");
            return;
        }

        try {
            FileInputStream fin = new FileInputStream(encryptedFile);
            BufferedReader br = new BufferedReader(new InputStreamReader(fin));
            String line;
            StringBuffer sb = new StringBuffer(TYPICAL_SIZE);
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }

            fin.close();
            br.close();

            if (sb.length() > 0) {
                byte[] encrypted = Base64.decodeBase64(sb.toString().getBytes());
//                byte[] decrypted = SymmetricCipher.desDecrypt(password, encrypted);
                byte[] decrypted = SymmetricCipher.aesDecrypt(password, encrypted);
                byte[] decompressed = Serializer.decompress(decrypted);
                JSONReader reader = new JSONReader();
                Object obj = reader.read(new String(decompressed));
                ApplicationAccountRecordSetBuilder builder = new ApplicationAccountRecordSetBuilder();
                ApplicationAccountRecordSet set = builder.build(obj);
                JSONValue json = JSONMapper.toJSON(set);
                String pp = json.render(true);
                FileOutputStream fout = new FileOutputStream(decryptedFile, false);
                fout.write(pp.getBytes());
                fout.close();
            }
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
